﻿/******************
 * 
 * 通用TCP客户端
 * 
 * ****************/

using Serilog.Core;
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace GsOrganization.TcpClient
{
    /// <summary>
    /// 通用TCP客户端
    /// </summary>
    public class CommonTcpClientLib
    {
        private IPEndPoint ServerIPEndPoint { get; set; }
        public Action<byte[]> OnRecvServerData { get; set; }
        public Action<string,string> OnConnectionClose { get; set; }

        private ReaderWriterLock RWL_ClientSocket { get; set; } = new ReaderWriterLock();

        public Socket ClientSocket;
        private byte[] bsBuffer=new byte[1024];
        private string tcpid;
        public CommonTcpClientLib(IPAddress ServerIP,int ServerPort, Action<byte[]> OnRecvServerData, Action<string,string> OnConnectionClose,string tcpid)
        {
                this.tcpid = tcpid;
            this.OnConnectionClose = OnConnectionClose;
            this.OnRecvServerData= OnRecvServerData;
            ServerIPEndPoint= new IPEndPoint(ServerIP, ServerPort);
            ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }
        private AutoResetEvent ARE_Connect = new AutoResetEvent(false);
        public bool Connect(int TimeOut=20000)
        {
            CancellationTokenSource tokenSource = new CancellationTokenSource();

            // create the cancellation token
            CancellationToken token = tokenSource.Token;
            SocketAsyncEventArgs args = new SocketAsyncEventArgs()
            {
                RemoteEndPoint = ServerIPEndPoint,

            };
            args.Completed += Args_Completed;
            ClientSocket.ConnectAsync(ServerIPEndPoint,token).GetAwaiter().OnCompleted(()=>
            {
                ARE_Connect.Set();
            });
           
            var info = ARE_Connect.WaitOne(TimeOut);
            if (ClientSocket.Connected)
            {
                
                try
                {
                    //this.ClientSocket.Connect(this.ServerIPEndPoint);
                    ClientSocket.BeginReceive(bsBuffer, 0, 1024, SocketFlags.None, new AsyncCallback(RecvSocketDataCallBack), null);
                    return true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    return false;
                }
            }
            else
            {
                try
                {
                    tokenSource.Cancel();
                }
                catch
                { }
                try
                {
                    ClientSocket.Close();
                }
                catch
                { }
                try
                {
                    ClientSocket.Dispose();
                }
                catch
                { }
                return false;
            }
           
        }

     
        private void Args_Completed(object? sender, SocketAsyncEventArgs e)
        {
            ARE_Connect.Set();
            if (e.SocketError == SocketError.Success)
            {
                
            }
            
            //Console.WriteLine("Args_Completed");
            //Console.WriteLine(e.SocketError);
            //throw new NotImplementedException();
        }

        public void ConnectComplete()
        {
            
        }

        public void Close()
        {
            RWL_ClientSocket.AcquireWriterLock(-1);
            try
            {
                if (ClientSocket != null)
                {
                    ClientSocket.Close();
                    ClientSocket.Dispose();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            RWL_ClientSocket.ReleaseWriterLock();
        }


        public int SendMessage(byte[] bsData)
        {
            int SendCnt = 0;
            RWL_ClientSocket.AcquireWriterLock(-1);
            try
            {
                var dsfa = ClientSocket.Connected;
                //Connect();
                SendCnt = ClientSocket.Send(bsData);
                var asdf = 0;

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                //Logger.Error(ex, "StartServerSocket error");
                SendCnt = -1;
            }
            RWL_ClientSocket.ReleaseWriterLock();
            return SendCnt;
        }

        public int SendMessage(byte[] bsData,int DelaySecodes)
        {
            int SendCnt = 0;
            RWL_ClientSocket.AcquireWriterLock(-1);
            Thread.Sleep(DelaySecodes * 1000);
            try
            {
                SendCnt = ClientSocket.Send(bsData);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                
                SendCnt = -1;
            }
            //System.Threading.Thread.Sleep(DelaySecodes * 500);
            RWL_ClientSocket.ReleaseWriterLock();
            return SendCnt;
        }

        private void RecvSocketDataCallBack(IAsyncResult ar)
        {
            int RecvDataLen = 0;
            RWL_ClientSocket.AcquireWriterLock(-1);
            try
            {
                RecvDataLen= ClientSocket.EndReceive(ar);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                RWL_ClientSocket.ReleaseWriterLock();
                Close();
                OnConnectionClose(ex.Message,tcpid);
                return;
            }
            RWL_ClientSocket.ReleaseWriterLock();
            if (RecvDataLen == 0)
            {
                Close();
                OnConnectionClose("Recv Data Size=0",tcpid);
                return;
            }
            byte[] bsData=new byte[RecvDataLen];
            Array.Copy(bsBuffer,0,bsData,0,RecvDataLen);
            OnRecvServerData(bsData);
            RWL_ClientSocket.AcquireWriterLock(-1);
            try
            {
                ClientSocket.BeginReceive(bsBuffer, 0, 1024, SocketFlags.None, new AsyncCallback(RecvSocketDataCallBack), null);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                RWL_ClientSocket.ReleaseWriterLock();
                Close();
                OnConnectionClose(ex.Message, tcpid);
                return;
            }
            RWL_ClientSocket.ReleaseWriterLock();
        }
    }
}